package com.xuzimian.globaldemo.designpatterns.proxy.dynamic.performance;

import com.xuzimian.globaldemo.designpatterns.proxy.ISubject;
import com.xuzimian.globaldemo.designpatterns.proxy.dynamic.performance.handler.CglibHandler;
import com.xuzimian.globaldemo.designpatterns.proxy.dynamic.performance.handler.JavassistHandler;
import com.xuzimian.globaldemo.designpatterns.proxy.dynamic.performance.handler.JdkHandler;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtNewConstructor;
import javassist.CtNewMethod;
import javassist.util.proxy.ProxyObject;
import net.sf.cglib.proxy.Enhancer;

import java.lang.reflect.Proxy;

import static java.lang.String.format;

public class ProxyFactory {

    public static ISubject createJdkProxy() {
        ISubject jdkProxy = (ISubject) Proxy.newProxyInstance(
                ClassLoader.getSystemClassLoader(),
                new Class[]{ISubject.class},
                new JdkHandler(new ISubject() {
                    @Override
                    public String processBusiness() {
                        return "Jdk";
                    }
                }));
        return jdkProxy;
    }

    public static ISubject createCglibProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setCallback(new CglibHandler(new ISubject() {
            @Override
            public String processBusiness() {
                return "Cglib";
            }
        }));
        enhancer.setInterfaces(new Class[]{ISubject.class});
        ISubject cglibProxy = (ISubject) enhancer.create();
        return cglibProxy;
    }

    public static ISubject createJavassistDynProxy() {
        javassist.util.proxy.ProxyFactory proxyFactory = new javassist.util.proxy.ProxyFactory();

        proxyFactory.setInterfaces(new Class[]{ISubject.class});

        Class proxyClass = proxyFactory.createClass();

        ISubject javassistProxy = null;

        try {
            javassistProxy = (ISubject) proxyClass.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }

        assert javassistProxy != null;
        ((ProxyObject) javassistProxy).setHandler(new JavassistHandler(new ISubject() {
            @Override
            public String processBusiness() {
                return "Javassist";
            }
        }));
        return javassistProxy;
    }

    public static ISubject createJavassistBytecodeDynamicProxy() {
        ClassPool classPool = new ClassPool(true);
        CtClass ctClass = classPool.makeClass(ISubject.class.getName() + "JavaassistBytecodeProxy");

        try {
            ctClass.addInterface(classPool.get(ISubject.class.getName()));

            ctClass.addConstructor(CtNewConstructor.defaultConstructor(ctClass));

            ctClass.addField(CtField.make(format("public %s real;", ISubject.class.getName()), ctClass));

            ctClass.addMethod(CtNewMethod.make(format("public String processBusiness() { if(real==null)real=new %s();return real.processBusiness(); }",
                    Subject.class.getName()), ctClass));

            return (ISubject) ctClass.toClass().newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
